package com.giaothong.http.core;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.net.UnknownHostException;
import java.security.KeyStore;
import java.util.ArrayList;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpVersion;
import org.apache.http.NameValuePair;
import org.apache.http.StatusLine;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.conn.scheme.PlainSocketFactory;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;
import org.apache.http.params.HttpProtocolParams;
import org.apache.http.protocol.HTTP;
import org.json.JSONObject;

import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Context;
import android.os.AsyncTask;
import android.util.Log;

import com.giaothong.R;
import com.giaothong.utils.ALog;

/*
 * @param <T> class will be handle action xml reader & parser
 */
public abstract class NewHttpConnectionAsync extends AsyncTask<String, Void, JSONObject> {

	// store param
	private ArrayList<NameValuePair> params; // add param get method
	private ArrayList<NameValuePair> paramsMedias;// add parman post method

	public void addParam(String name, String value) {
		params.add(new BasicNameValuePair(name, value));
	}

	public void addParamMedia(String name, String value) {
		paramsMedias.add(new BasicNameValuePair(name, value));
	}

	private HttpConnectionId requestCode;
	private HttpResult resultCode;
	private HttpAsyncCallback<JSONObject> callback;

	private Context context = null;
	private ProgressDialog progressDialog = null;
	private boolean isShowProgressDialogWhenLoading = true;

	protected StringEntity se;
	private HttpClient client;

	/**
	 * Request params (JSONObject)
	 */
	protected JSONObject rootObj;// = new JSONObject();

	// Set the timeout in milliseconds until a connection is established.
	private static int TIME_OUT_CONNECTION = 10000;

	// Set the default socket timeout in milliseconds which is the timeout for
	// waiting for data.
	private static int TIME_OUT_SOCKET = 120000;

	/**
	 * Construct a new task with a callback to invoke when the request complete
	 * or fails.
	 * 
	 * @param callback
	 */
	protected NewHttpConnectionAsync(Context context, HttpConnectionId httpConnectionId, HttpAsyncCallback<JSONObject> callback) {
		this.requestCode = httpConnectionId;
		this.resultCode = HttpResult.SUCCESS;
		this.callback = callback;
		this.client = getNewHttpClient();
		this.isShowProgressDialogWhenLoading = true;

		this.context = context;
		progressDialog = new ProgressDialog(this.context);
		progressDialog.setCanceledOnTouchOutside(false);
		params = new ArrayList<NameValuePair>();
		paramsMedias = new ArrayList<NameValuePair>();
	}

	protected void onPreExecute() {
		if (isShowProgressDialogWhenLoading) {
			if (context == null) {
				return;
			}
			Activity activity = (Activity) context;
			if (activity.isFinishing()) {
				return;
			}

			progressDialog.setTitle("");
			progressDialog.setMessage(context.getString(R.string.please_wait));
			progressDialog.show();
		}
	}

	/*
	 * (non-Javadoc)
	 * @see android.os.AsyncTask#doInBackground(Params[])
	 */
	@Override
	protected JSONObject doInBackground(String... params) {
		try {
			// Get url from params
			String url;
			if (params != null && params.length > 0) {
				url = params[0];
			} else {
				this.resultCode = HttpResult.UNKNOWN_ERROR;
				cancel(true);
				return null;
			}

			String json = getJSONDataFromUrl(url);

			if (json == null) {
				if (this.resultCode == HttpResult.SUCCESS)
					this.resultCode = HttpResult.JSON_PARSE_ERROR;
				return null;
			}
			ALog.d("json", json);
			// Parse xml
			JSONObject jsonObj = null;
			if (json != null) {
				try {
					jsonObj = new JSONObject(json);
				} catch (Exception e) {
					this.resultCode = HttpResult.JSON_PARSE_ERROR;
				}

				return jsonObj;
			}
		} catch (Exception e) {
			this.resultCode = HttpResult.UNKNOWN_ERROR;
		}
		return null;
	}

	protected void onPostExecute(JSONObject result) {
		if ((result == null) && (resultCode == HttpResult.SUCCESS)) {
			this.resultCode = HttpResult.UNKNOWN_ERROR;
			callback.onTaskComplete(requestCode, resultCode, result);
		} else {
			callback.onTaskComplete(requestCode, resultCode, result);
		}

		progressDialog.dismiss();
	}

	protected String getJSONDataFromUrl(String url) {
		if ((url == null) || (url.length() == 0))
			return null;

		// add parameters in url
		String combinedParams = "";
		if (!params.isEmpty()) {
			// combinedParams += "?";
			for (NameValuePair p : params) {
				String paramString = "";
				try {
					paramString = p.getName() + "=" + URLEncoder.encode(p.getValue(), "UTF-8");
				} catch (UnsupportedEncodingException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				if (combinedParams.length() > 1) {
					combinedParams += "&" + paramString;
				} else {
					combinedParams += paramString;
				}
			}
		}

		// add param to url
		url = url + combinedParams;

		ALog.i("url", url);

		StringBuilder builder = new StringBuilder();
		HttpPost httpPost = new HttpPost(url);

		//
		if (rootObj != null) {
			try {
				if (paramsMedias.size() != 0) {
					se = new UrlEncodedFormEntity(paramsMedias);
				} else {
					se = new StringEntity("", HTTP.UTF_8);
					se.setContentType("text/xml; charset=utf-8");
				}
			} catch (UnsupportedEncodingException e) {
				Log.e("ERROR", "UnsupportedEncodingException");
				e.printStackTrace();
			}
		}

		if (se == null) {
			this.resultCode = HttpResult.UNKNOWN_ERROR;
			return null;
		}
		try {
			httpPost.setEntity(se);
			HttpResponse response = client.execute(httpPost);

			// String responseBody = EntityUtils.toString(response.getEntity());
			// Log.d("RESPONSE", responseBody);

			StatusLine statusLine = response.getStatusLine();
			int statusCode = statusLine.getStatusCode();
			if (statusCode == 200) {
				HttpEntity entity = response.getEntity();
				InputStream content = entity.getContent();
				BufferedReader reader = new BufferedReader(new InputStreamReader(content));
				String line;
				while ((line = reader.readLine()) != null) {
					builder.append(line);
				}
			} else {
				return null;

			}
		} catch (UnknownHostException e) {
			this.resultCode = HttpResult.CONNECTION_ERROR;
			return null;
		} catch (ClientProtocolException e) {
			this.resultCode = HttpResult.CONNECTION_ERROR;
			return null;
		} catch (IOException e) {
			this.resultCode = HttpResult.TIMEOUT_ERROR;
			return null;
		}

		ALog.d("RESPONSE", builder.toString());
		return builder.toString();
	}

	private DefaultHttpClient getNewHttpClient() {
		try {
			KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
			trustStore.load(null, null);

			SSLSocketFactory sf = new NewSSLSocketFactory(trustStore);
			sf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);

			HttpParams params = new BasicHttpParams();
			HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
			HttpProtocolParams.setContentCharset(params, HTTP.UTF_8);

			// Set the timeout in milliseconds until a connection is
			// established.
			// The default value is zero, that means the timeout is not used.
			HttpConnectionParams.setConnectionTimeout(params, TIME_OUT_CONNECTION);
			// Set the default socket timeout (SO_TIMEOUT)
			// in milliseconds which is the timeout for waiting for data.

			HttpConnectionParams.setSoTimeout(params, TIME_OUT_SOCKET);

			SchemeRegistry registry = new SchemeRegistry();
			registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
			registry.register(new Scheme("https", sf, 443));

			ClientConnectionManager ccm = new ThreadSafeClientConnManager(params, registry);

			return new DefaultHttpClient(ccm, params);
		} catch (Exception e) {
			return new DefaultHttpClient();
		}

	}

	public void setIsShowProgressDialogWhenLoading(boolean value) {
		isShowProgressDialogWhenLoading = value;
	}
}
